import { _decorator, Node, Enum, instantiate, resources, Prefab, Component } from 'cc';
import ArrayWatcher from '../../libs/watch/ArrayWatcher';
import PokerItem from '../data/PokerItem';
import GashaponWatcher from '../../libs/watch/GashaponWatcher';
import PokerArray from '../data/PokerArray';
import Deck from '../data/Deck';
import { Level } from '../data/Level';
import { PokerConfig } from '../config/PokerConfig';
import ValueWatcher from '../../libs/watch/ValueWatcher';
import { PokerIntensify } from '../data/PokerIntensify';
import NumberWatcher from '../../libs/watch/NumberWatcher';
import { StorageKV } from '../core/storage/StorageKV';
import { StorageConfig } from '../config/AppConfig';
import EventEmitter from '../../libs/eventemitter/EventEmitter';
import { MagicPoker } from './MagicManager';
import PokerCompose from '../data/PokerCompose';
import { RecycleComponent } from '../core/RecycleComponent';
import NodeGroup from '../core/gui/pool/NodeGroup';
import { listenerClick } from '../core/EventHelper';

export enum GameState {
    ready,
    start,
    resume,
    pause,
    stop,
    finish,
}

export enum GameStep {
    Poker_Init = 'Poker_Init',// 初次上牌时
    Poker_Recycle = 'Poker_Recycle',// 换牌:将旧牌回收到回收库
    Poker_Replace = 'Poker_Replace',// 换牌:将牌库选取若干牌到手牌
    Poker_Play = 'Poker_Play',// 打牌,将手牌进行打出到桌面
    Poker_Magic = 'Poker_Magic',// 使用道具牌
    Poker_Finish = 'Poker_Finish',// 出牌完成
    Poker_Statistic_Single = 'Poker_Statistic_Single',// 出牌完成
    Poker_Statistic_Array = 'Poker_Statistic_Array',// 出牌完成
    Poker_Statistic_Compose = 'Poker_Statistic_Compose',// 出牌完成
    Poker_Settlement = 'Poker_Settlement',// 结算利息
}

Enum(GameStep);

export type ScoreCall = (valueDiff: number, multipleDiff: number) => void;

export class GameManager extends EventEmitter {

    readonly level: Level;
    readonly deck: Deck;

    readonly storage: StorageKV.Manager;

    readonly gashaponLibrary: GashaponWatcher;
    readonly gashaponRecycler: GashaponWatcher;

    readonly watchLevelScore: NumberWatcher;
    readonly watchCurrentScore: NumberWatcher;
    readonly watchCurrentValue: NumberWatcher;
    readonly watchCurrentMultiple: NumberWatcher;

    readonly watchExpectValue: NumberWatcher;
    readonly watchExpectMultiple: NumberWatcher;

    readonly watchMoney: NumberWatcher;
    readonly watchReplacePoker: NumberWatcher;

    readonly watchMineSelect: ArrayWatcher<PokerItem>;
    readonly watchMine: ArrayWatcher<PokerItem>;
    readonly watchTable: ArrayWatcher<PokerArray>;

    readonly watchGame: ValueWatcher<GameState>;

    private readonly poolPoker = new NodeGroup(RecycleComponent);

    readonly host: Component;

    constructor(host: Component, level: Level, deck: Deck) {
        super()
        this.host = host;
        this.level = level;
        this.deck = deck;

        this.storage = new StorageKV.Manager();

        //弃牌牌库
        const deckObj = deck.pokers.reduce((pre: any, value, idx) => {
            pre[value.poker] = 1;
            return pre;
        }, {});
        this.gashaponRecycler = new GashaponWatcher(deckObj);

        // 正常牌库
        this.gashaponLibrary = new GashaponWatcher(deckObj)

        // 游戏状态
        this.watchGame = new ValueWatcher(GameState.ready);

        // 计分的Watcher
        this.watchLevelScore = new NumberWatcher(this.level.score);
        // 当前关卡得分
        this.watchCurrentScore = new NumberWatcher(0);
        // 当前的分值
        this.watchCurrentValue = new NumberWatcher(0);
        // 当前的倍率
        this.watchCurrentMultiple = new NumberWatcher(1);

        // 用户金币
        this.watchMoney = new NumberWatcher(this.storage.getNumber(StorageConfig.key_money));
        // 换牌次数
        this.watchReplacePoker = new NumberWatcher(5);

        // 预测 Value/Multiple
        this.watchExpectValue = new NumberWatcher(0);
        this.watchExpectMultiple = new NumberWatcher(0);

        // 卡牌
        this.watchMineSelect = new ArrayWatcher();
        this.watchMine = new ArrayWatcher();
        this.watchTable = new ArrayWatcher();
    }


    /**
    * 返回指定牌库的随机的一张牌
    */
    pokerNext(nextCall: ((next: PokerConfig.EnumPoker) => void), gashapon: GashaponWatcher, ...hopes: (number | PokerConfig.EnumPoker)[]) {
        console.log('pokerNext', ...hopes);
        for (const hope of hopes) {
            if (typeof hope === 'number') {
                const hopePokers = PokerConfig.pokerEnumFromNumber(hope);
                for (const element of hopePokers) {
                    const poker = gashapon.next(element);
                    if (poker) {
                        nextCall(poker as PokerConfig.EnumPoker);
                        return;
                    }
                }
            }

            if (typeof hope === 'string') {
                const poker = gashapon.next(hope);
                if (poker) {
                    nextCall(poker as PokerConfig.EnumPoker);
                    return;
                }
            }
        }

        const poker = gashapon.next();
        if (poker) {
            nextCall(poker as PokerConfig.EnumPoker);
            return;
        }

        // 检查目前是否能够胡牌
        if (gashapon === this.gashaponLibrary) {
            //进行提示用户 还能成牌
            const pokerSuggest = this.checkMineAndTable();
            if (pokerSuggest.length === 0) {
                // 无法成牌,结束
                this.watchGame.change(GameState.finish);
                return;
            }
            // 进行推荐
            this.watchMineSelect.replaceAll(pokerSuggest)
        }
    }

    pokerNextIntensify(nextCall: ((next: PokerIntensify) => void), ...hopes: (number | PokerConfig.EnumPoker)[]) {
        this.pokerNext((next) => { nextCall(this.deck.getPokerIntensify(next)) }, this.gashaponLibrary, ...hopes);
    }

    async pokerNextItem(...hopes: (number | PokerConfig.EnumPoker)[]): Promise<PokerItem> {
        return new Promise((confirm) => {
            this.pokerNextIntensify(async (next) => {
                const item = await this.poolPokerReuse(next);
                const unbind = listenerClick(item.pokerNode, () => {
                    if (this.watchMine.include(item)) {
                        if (this.watchMineSelect.include(item)) {
                            this.watchMineSelect.remove(item);
                        } else {
                            this.watchMineSelect.push(item);
                        }
                    }
                })
                item.pokerNode.once('onRecycle', () => {
                    unbind()
                })
                this.watchMine.push(item);
                confirm(item);
            }, ...hopes);
        })
    }

    pokerRecycler(objs: PokerConfig.EnumPoker | PokerIntensify, gashapon: GashaponWatcher = this.gashaponRecycler) {
        gashapon.putBack(objs instanceof PokerIntensify ? objs.poker : objs, 1);
    }


    checkMineAndTable(): PokerItem[] {
        //chech mine has eye
        const pokerEyeAce = this.watchMine.filter(v => PokerConfig.pokerIsAce(v.getPoker()));
        const pokerEyeJoker = this.watchMine.filter(v => PokerConfig.pokerIsJoker(v.getPoker()));
        if (pokerEyeAce.length === 0 && pokerEyeJoker.length === 0) {
            return [];
        }

        // 判断手牌可成的大于1或者桌牌已有的大于1
        if (this.watchTable.length() > 0) {
            return [pokerEyeAce[0] ?? pokerEyeJoker[0]];
        }

        // 寻找目前是否存在不需要joker便可成的拍
        const pokerWithoutJoker = this.watchMine.filter((v) => {
            return pokerEyeJoker.indexOf(v) < 0;
        }).sort((v1, v2) => {
            return PokerConfig.pokerNumber(v1.getPoker()) - PokerConfig.pokerNumber(v2.getPoker());
        });

        for (let index = 2; index < pokerWithoutJoker.length; index++) {
            const poker_item_array = [pokerWithoutJoker[index - 2], pokerWithoutJoker[index - 1], pokerWithoutJoker[index]];
            const poker_enum_array = poker_item_array.map(v => v.getPoker())
            if (PokerConfig.pokerIsChain(poker_enum_array)) {
                return poker_item_array
            }

            if (PokerConfig.pokerIsPairs(poker_enum_array)) {
                return poker_item_array
            }
            if (pokerEyeJoker.length > 0) {
                const poker_item_matrix = poker_item_array.map((v, idx, array) => {
                    if (idx === array.length - 1) {
                        return [array[idx], array[0]];
                    }
                    return [array[idx], array[idx + 1]];
                });

                // 寻找间接通过 joker变换
                // 判断是否是对子
                for (const poker_item_array of poker_item_matrix) {
                    if (poker_item_array.length !== 2) continue;
                    const space = Math.abs(poker_item_array[1].getNumber() - poker_item_array[0].getNumber());
                    if (space === 0 || space === 1 || space === 2) {
                        // 对子
                        return poker_item_array;
                    }
                }
            }
        }
        return [];
    }

    //#region Game
    /**
     * 下一等级
     */
    gameNext() {
        // 跳转到 用户结算和成长页面
        throw 'gameNext'
    }

    /**
     * 游戏结束
     */
    gameOver() {
        // 跳转结算展示. 添加按钮展示
        throw 'gameOver'
    }



    //#region Step Init
    // 监听和发布::[初次上牌]
    emitInit() {
        this.watchGame.change(GameState.start);
        this.emit(GameStep.Poker_Init, this);
        return this;
    }

    listenerInit(call: (game: GameManager) => void) {
        this.once(GameStep.Poker_Init, call, this);
        return () => {
            this.off(GameStep.Poker_Init, call, this, true);
        }
    }

    //#region Step Recycle
    // 监听和发布::[换牌:手牌->回收库]
    emitRecycle(poker: PokerItem) {
        this.emit(GameStep.Poker_Recycle, this, poker);
        return this;
    }

    listenerRecycle(call: (game: GameManager) => void) {
        this.on(GameStep.Poker_Recycle, call, this);
        return () => {
            this.off(GameStep.Poker_Recycle, call, this);
        }
    }

    //#region Step Replace
    // 监听和发布::[换牌:牌库->手牌]
    emitReplace(poker: PokerItem) {
        this.emit(GameStep.Poker_Replace, this, poker);
        return this;
    }

    listenerReplace(call: (game: GameManager) => void) {
        this.on(GameStep.Poker_Replace, call, this);
        return () => {
            this.off(GameStep.Poker_Replace, call, this);
        }
    }


    //#region Step Play
    // 监听和发布::[出牌]
    emitPlay(array: PokerArray) {
        this.emit(GameStep.Poker_Play, this, array);
        return this;
    }

    listenerPlay(call: (game: GameManager, array: PokerArray) => void) {
        this.on(GameStep.Poker_Play, call, this);
        return () => {
            this.off(GameStep.Poker_Play, call, this);
        }
    }

    //#region Step Magic
    // 监听和发布::[魔法牌执行]
    emitMagic(magic: MagicPoker) {
        this.emit(GameStep.Poker_Magic, this, magic);
        return this;
    }

    listenerMagic(call: (game: GameManager, magic: MagicPoker) => void) {
        this.on(GameStep.Poker_Magic, call, this);
        return () => {
            this.off(GameStep.Poker_Magic, call, this);
        }
    }

    //#region Step Finish
    // 监听和发布::[胡牌]
    emitFinish(compose: PokerCompose) {
        this.watchGame.change(GameState.finish);
        this.emit(GameStep.Poker_Finish, this, compose);
        return this;
    }

    listenerFinish(call: (game: GameManager, compose: PokerCompose) => void) {
        this.on(GameStep.Poker_Finish, call, this);
        return () => {
            this.off(GameStep.Poker_Finish, call, this);
        }
    }


    //#region Step Statistic
    // 监听和发布::[开始计分:单牌]
    emitStatisticSingle(poker: PokerItem, call?: ScoreCall) {
        this.emit(GameStep.Poker_Statistic_Single, this, poker, call);
        return this;
    }

    listenerStatisticSingle(call: (game: GameManager, poker: PokerItem, call?: ScoreCall) => void) {
        this.on(GameStep.Poker_Statistic_Single, call, this);
        return () => {
            this.off(GameStep.Poker_Statistic_Single, call, this);
        }
    }

    // 监听和发布::[开始计分:顺子/对子/杆子]
    emitStatisticArray(pokers: PokerArray, call?: ScoreCall) {
        this.emit(GameStep.Poker_Statistic_Array, this, pokers, call);
        return this;
    }

    listenerStatisticArray(call: (game: GameManager, pokers: PokerArray, call?: ScoreCall) => void) {
        this.on(GameStep.Poker_Statistic_Array, call, this);
        return () => {
            this.off(GameStep.Poker_Statistic_Array, call, this);
        }
    }

    // 监听和发布::[开始计分:组合]
    emitStatisticCompose(pokers: PokerCompose, call?: ScoreCall) {
        this.emit(GameStep.Poker_Statistic_Compose, this, pokers, call);
        return this;
    }

    listenerStatisticCompose(call: (game: GameManager, pokers: PokerCompose, call?: ScoreCall) => void) {
        this.on(GameStep.Poker_Statistic_Compose, call, this);
        return () => {
            this.off(GameStep.Poker_Statistic_Compose, call, this);
        }
    }

    //#region Step Settlement
    // 监听和发布::[金币结算]
    emitSettlement(level: boolean) {
        this.emit(GameStep.Poker_Settlement, this, level);
        return this;
    }

    listenerSettlement(call: (game: GameManager, level: boolean) => void) {
        this.on(GameStep.Poker_Settlement, call, this);
        return () => {
            this.off(GameStep.Poker_Settlement, call, this);
        }
    }

    //#region Promise Queue
    readonly queue: Function[] = [];
    addComputing(call: Function) {
        this.queue.push(call)
    }

    async postRunning() {
        return new Promise(async (sure) => {
            while (this.queue.length > 0) {
                await this.queue.shift()?.();
            }
            sure(undefined);
        })
    }


    async nodePokerCreator() {
        return new Promise<Node>((confirm, rejuse) => {
            resources.load('prefab/Poker', Prefab, (error, prefab) => {
                if (error) {
                    rejuse(error);
                    return;
                }
                confirm(instantiate(prefab));
            })
        })
    }

    //#region Node池
    async poolPokerReuse(poker: PokerItem | PokerIntensify | PokerConfig.EnumPoker) {
        const node = this.poolPoker.get() || await this.nodePokerCreator();
        if (poker instanceof PokerItem) {
            return new PokerItem(poker.pokerIntensify, node);
        } else if (poker instanceof PokerIntensify) {
            return new PokerItem(poker, node);
        } else {
            return new PokerItem(this.deck.getPokerIntensify(poker), node);
        }
    }

    poolPokerRecycle(poker: Node | PokerItem) {
        // poker instanceof Node ? poker.destroy() : poker.pokerNode.destroy()
        this.poolPoker.put(poker instanceof Node ? poker : poker.pokerNode);
    }

    poolPokerSize() {
        return this.poolPoker.size()
    }
}

